#!/bin/sh

#this script is brutal and verbose, has no tricks and is quite linear, then
#quite easy to deal with
#for the moment, it's hardcoded for a gcc toolchain... BAD! Since now
#gcc is a c++ piece of shit

# stolen from ffmpeg configure like a pig
set -e

# Prevent locale nonsense from breaking basic text processing.
LC_ALL=C
export LC_ALL

libudev_api=0
fake_root=fake_root

#-------------------------------------------------------------------------------
libudev_src_files='
libudev.c
libudev-device.c
libudev-device-private.c
libudev-enumerate.c
libudev-list.c
libudev-monitor.c
libudev-queue.c
libudev-queue-private.c
libudev-util.c
libudev-util-private.c
'

udevd_common_src_files='
udev-event.c
udev-watch.c
udev-node.c
udev-rules.c
udev-ctrl.c
udev-builtin.c
udev-builtin-blkid.c
udev-builtin-firmware.c
udev-builtin-hwdb.c
udev-builtin-input_id.c
udev-builtin-kmod.c
udev-builtin-path_id.c
udev-builtin-usb_id.c
'

udevd_src_files='
udevd.c
'

udevadm_src_files='
udevadm.c
udevadm-info.c
udevadm-control.c
udevadm-monitor.c
udevadm-settle.c
udevadm-trigger.c
udevadm-test.c
udevadm-test-builtin.c
'

#-------------------------------------------------------------------------------

clean_do()
{
    all_src_files="$libudev_src_files $udevd_common_src_files $udevd_src_files $udevadm_src_files"

    rm -Rf "$fake_root"
    for src_file in $all_src_files
    do
      pp_file=${src_file%.c}
      pp_file=${pp_file}.pp.c
      rm -f ${pp_file}
      o_file=${src_file%.c}
      o_file=${o_file}.o
      rm -f ${o_file}
      #clean directories, but keep root of build tree
      tgt_dir=$(dirname $src_file)
      if test -d $tgt_dir -a "$tgt_dir" != "."; then
        rmdir --ignore-fail-on-non-empty -p $tgt_dir
      fi
    done
    exit 0
}

sep_start()
{
  printf '###############################################################################\n'
}

sep_end()
{
  printf '###############################################################################\n\n'
}

subsep_start()
{
  printf '*******************************************************************************\n'
}

subsep_end()
{
  printf '*******************************************************************************\n'
}

################################################################################

is_in(){
    value=$1
    shift
    for var in $*; do
        [ $var = $value ] && return 0
    done
    return 1
}

die_unknown(){
    echo "Unknown option \"$1\"."
    echo "See $0 --help for available options."
    exit 1
}

set_default(){
    for opt; do
        eval : \${$opt:=\$${opt}_default}
    done
}

CMDLINE_SET='
    bin_cpp
    bin_cc
    bin_ccld
    libudev_cpp
    libudev_cc
    libudev_ccld
    prefix
    eprefix
    libdir
    includedir
    sysconfdir
    pkglibexecdir
    firmware_path
    usb_database
    pci_database
    version
'

################################################################################

#command line set defaults
#-------------------------------------------------------------------------------
#This defaults are for gcc, tested with version 4.7.3. You will need to
#override those for you compiler (tinycc/open64/pcc...). Additionnally, source
#support for different toolchains is not done.
#The right way to do it is to have a toolchain abstraction layer since there are
#no accurate enough standards
bin_cpp_default='gcc -E -Wall -Wextra -D_GNU_SOURCE'
bin_cc_default='gcc -Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -std=gnu99 -O0 -c'
bin_ccld_default='gcc -Wl,-s'

libudev_cpp_default='gcc -E -Wall -Wextra -D_GNU_SOURCE'
libudev_cc_default='gcc -Wall -Wextra -Wno-unused-parameter -std=gnu99 -O0 -fPIC -c'
libudev_ccld_default="gcc -shared -Wl,-s,-h,libudev.so.$libudev_api -lc -lrt"
#-------------------------------------------------------------------------------

prefix_default=/usr/local
eprefix_default='$prefix'
libdir_default='$eprefix/lib'
includedir_default='$prefix/include'
sysconfdir_default='$prefix/etc'
pkglibexecdir_default='$libdir/udev'
#MUST PUT A / AT THE END OR WON'T WORK!
firmware_path_default='$libdir/firmware/'
usb_database_default='$prefix/share/hwdata/usb.ids'
pci_database_default='$prefix/share/hwdata/pci.ids'
version_default=189
set_default $CMDLINE_SET

################################################################################

show_help(){
    cat <<EOF
Usage: make [options] [operations]

Operations: [default is to build libudev, udevd and udevadm]:
  clean                                clean build products

Options: [defaults in brackets after descriptions]

Help options:
  --help                               print this message

Standard options:
  --enable-logging                     enable logging code paths
  --enable-debug                       enable debug code paths

  --prefix=PREFIX                      architecture independent prefix [$prefix_default]
  --eprefix=EPREFIX                    architecture dependent exec prefix [$eprefix_default]
  --libdir=DIR                         object code libraries [$libdir_default]
  --includedir=DIR                     C header files [$includedir_default]
  --sysconfdir=DIR                     read-only single-machine data [$sysconfdir_default]
  --pkglibexecdir=DIR                  program executables [$pkglibexecdir_default]

  --firmware-path=PATH                 where to look for firmware when linux is asking udevd as a helper [$firmware_path_default]
  --usb-database=FILE_PATH             the usb database path [$usb_database_default]
  --pci-databased=FILE_PATH            the pci database path [$pci_database_default]

  --version=VERSION                    override the version number [$version_default]

  --man                                build man pages

Advanced options:
  --bin-cpp=CPP                        use CPP compiler command line CPP for target binaries [$bin_cpp_default]
  --bin-cc=CC                          use C compiler command line CC for target binaries [$bin_cc_default]
  --bin-ccld=CCLD                      use linker command line CCLD for target binaries [$bin_ccld_default]

  --libudev-cpp=CPP                    use CPP compiler command line CPP for target libudev [$libudev_cpp_default]
  --libudev-cc=CC                      use C compiler command line CC for target libudev [$libudev_cc_default]
  --libudev-ccld=CCLD                  use linker command line CCLD for target libudev [$libudev_ccld_default]
EOF
  exit 0
}

################################################################################

for opt do
    optval="${opt#*=}"
    case "$opt" in
        clean) clean_do
        ;;
        --help|-h) show_help
        ;;
        --enable-logging)
            CPPFLAGS="$CPPFLAGS -DENABLE_LOGGING"
        ;;
        --enable-debug)
            CPPFLAGS="$CPPFLAGS -DENABLE_DEBUG"
        ;;
        --man)
            MAN=yes
        ;;
        *)
            optname=${opt%%=*}
            optname=${optname#--}
            optname=$(echo "$optname" | sed 's/-/_/g')
            if is_in $optname $CMDLINE_SET; then
                eval $optname='$optval'
            else
                die_unknown $opt
            fi
        ;;
    esac
done

################################################################################

path_expand()
{
    e_v=$1
    #we set a maximum expansion depth of 3
    for d in 1 2 3
    do
        e_v=$(eval echo "$e_v")
    done
    #get rid of ugly double // in path
    echo "$e_v" | sed -e 's%//%/%g'
}

sep_start;echo 'expanding final paths:'
e_prefix=$(path_expand "$prefix")
e_eprefix=$(path_expand "$eprefix")
e_libdir=$(path_expand "$libdir")
e_includedir=$(path_expand "$includedir")
e_sysconfdir=$(path_expand "$sysconfdir")
e_pkglibexecdir=$(path_expand "$pkglibexecdir")
e_firmware_path=$(path_expand "$firmware_path")
e_usb_database=$(path_expand "$usb_database")
e_pci_database=$(path_expand "$pci_database")
echo "prefix=$e_prefix"
echo "eprefix=$e_eprefix"
echo "libdir=$e_libdir"
echo "includedir=$e_includedir"
echo "sysconfdir=$e_sysconfdir"
echo "pkglibexecdir=$e_pkglibexecdir"
echo "firmware_path=$e_firmware_path"
echo "usb_database=$e_usb_database"
echo "pci_database=$e_pci_database"
sep_end

################################################################################

sep_start;echo 'firmware path processing:'
#must have an ending /
e_firmware_path=$(readlink -m $e_firmware_path)/
echo "firmware path is $e_firmware_path";sep_end

################################################################################

sep_start;echo 'looking for source path:'
if test -f make; then
    src_path=.
else
    src_path=$(cd $(dirname "$0"); pwd)
    echo "$src_path" | grep -q '[[:blank:]]' &&
        die "out of tree builds are impossible with whitespace in source path."
    test -e "$src_path/config.h" &&
        die "out of tree builds are impossible with config.h in source dir."
fi
echo "source path is $src_path";sep_end

################################################################################

CPPFLAGS="$CPPFLAGS -DSYSCONFDIR=\"$e_sysconfdir\""
CPPFLAGS="$CPPFLAGS -DPKGLIBEXECDIR=\"$e_pkglibexecdir\""
CPPFLAGS="$CPPFLAGS -DFIRMWARE_PATH=\"$e_firmware_path\""
CPPFLAGS="$CPPFLAGS -DUSB_DATABASE=\"$e_usb_database\""
CPPFLAGS="$CPPFLAGS -DPCI_DATABASE=\"$e_pci_database\""
CPPFLAGS="$CPPFLAGS -DVERSION=\"$e_version\""

################################################################################

. $src_path/make.libudev.sh
. $src_path/make.udevd_all.sh

################################################################################

sep_start;echo 'generating pkg-config files for libudev and udev'
mkdir -p -- "$fake_root/$e_libdir/pkgconfig"

cp -f $src_path/src/udev.pc.in "$fake_root/$e_libdir/pkgconfig/udev.pc"
sed -i "s%@VERSION@%$version%" "$fake_root/$e_libdir/pkgconfig/udev.pc"
sed -i "s%@pkglibexecdir@%$e_pkglibexecdir%" "$fake_root/$e_libdir/pkgconfig/udev.pc"

cp -f $src_path/src/libudev.pc.in "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sed -i "s%@VERSION@%$version%" "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sed -i "s%@prefix@%$e_prefix%" "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sed -i "s%@exec_prefix@%$e_eprefix%" "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sed -i "s%@libdir@%$e_libdir%" "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sed -i "s%@includedir@%$e_includedir%" "$fake_root/$e_libdir/pkgconfig/libudev.pc"
sep_end

################################################################################

sep_start;echo 'fake installing the system udev rules'
mkdir -p -- "$fake_root/$e_pkglibexecdir"
rm -Rf -- "$fake_root/$e_pkglibexecdir/rules.d"
cp -rf -- "$src_path/rules" "$fake_root/$e_pkglibexecdir/rules.d"
sep_end

################################################################################

if [ -z "$MAN" ]; then
    exit 0
fi
sep_start;echo 'generating man pages'
mkdir -p -- "$fake_root/$e_prefix/share/man8" "$fake_root/$e_prefix/share/man7"
xsltproc -o "$fake_root/$e_prefix/share/man7/udev.7" -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl "$src_path/src/udev.xml"
xsltproc -o "$fake_root/$e_prefix/share/man8/udevadm.8" -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl "$src_path/src/udevadm.xml"
xsltproc -o "$fake_root/$e_prefix/share/man8/udevd.8" -nonet http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl "$src_path/src/udev.xml"
sep_end
